﻿using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace System
{
    public class IntPtrHelper
    {
        #region 属性
        /// <summary>
        /// 字符串句柄集合
        /// </summary>
        private static readonly List<IntPtr> StringIntPtrs = new List<IntPtr>();

        /// <summary>
        /// 结构体集合
        /// </summary>
        private static readonly List<IntPtr> StructIntPtrs = new List<IntPtr>();
        #endregion

        #region 方法
        #region String IntPtr 转换
        /// <summary>
        /// 将字符串复制到非托管内存并返回指针。
        /// 该方法保证注销后清理分配的非托管内存。
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static IntPtr StringToIntPtr(string str)
        {
            IntPtr intp = IntPtr.Zero;
            try
            {
                intp = Marshal.StringToHGlobalAnsi(str);
                //这里加try,catch是因为StringIntPtrs在Add的时候会提示上限/下限问题(偶现)
                //奇怪的已经是添加到集合了,依然会报错,所以用try,catch包起来,防止出错
                StringIntPtrs?.Add(intp);
            }
            catch (Exception ex)
            {

            }
            return intp;
        }

        /// <summary>
        /// 指针转String
        /// </summary>
        /// <param name="ptr"></param>
        /// <returns></returns>
        public static string IntPtrToString(IntPtr ptr)
        {
            string ret = "";
            try
            {
                //用该方法，以保证外部不存在Marshal的直接引用，从而保证注销时清理所有非托管内存。
                ret = Marshal.PtrToStringAnsi(ptr);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            return ret;
        }
        #endregion

        #region 结构体 IntPtr 转换
        /// <summary>
        /// 结构体转化为指针
        /// </summary>
        /// <param name="objstruct">结构体</param>
        /// <param name="sizeLength">结构体大小</param>
        /// <returns>指针</returns>
        public static IntPtr StructToIntPtr(object objstruct, out int sizeLength)
        {
            IntPtr intp = IntPtr.Zero;
            try
            {
                int objsize = Marshal.SizeOf(objstruct);
                intp = Marshal.AllocCoTaskMem(objsize);
                Marshal.StructureToPtr(objstruct, intp, false);
                sizeLength = objsize;
                StructIntPtrs.Add(intp);
            }
            catch (Exception)
            {
                sizeLength = 0;
            }
            return intp;
        }
        #endregion

        /// <summary>
        /// 注销后清理非托管内存中的资源
        /// </summary>
        public static void Clean()
        {
            Task.Factory.StartNew(() =>
            {
                //释放非托管内存中的字符串空间
                var intps = StringIntPtrs.ToArray();
                foreach (var intPtr in intps)
                {
                    try
                    {
                        //StringIntPtrs在Add的时候会提示上限/下限问题(偶现)
                        //为防止出错,Remove也加上try,catch
                        StringIntPtrs.Remove(intPtr);
                    }
                    catch (Exception)
                    {
                    }
                    Marshal.FreeHGlobal(intPtr);
                }

                var intp1s = StructIntPtrs.ToArray();
                foreach (var intPtr in intp1s)
                {
                    try
                    {
                        //StringIntPtrs在Add的时候会提示上限/下限问题(偶现)
                        //为防止出错,Remove也加上try,catch
                        StructIntPtrs.Remove(intPtr);
                    }
                    catch (Exception)
                    {
                    }
                    Marshal.FreeHGlobal(intPtr);
                }
            });
        } 
        #endregion
    }
}
